package pl.polidea.imf.common.java.impl.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

import pl.polidea.imf.blackberry.util.DeviceStatusHelper;
import pl.polidea.imf.blackberry.util.HttpHelper;
import pl.polidea.imf.common.java.GlobalConfig;
import pl.polidea.imf.common.java.http.GenericHttpConnection;

public class GenericHttpConnectionImpl extends GenericHttpConnection {

	static final long HTTP_CONNECTION_TIMEOUT = 30 * 1000;

	private HttpConnection connection;
	private ConnectionTimeOutWatchDog timeoutWatchDog;

	private static String connectionAppendix;
	private static boolean isValidConnectionAvailable = false;
	private OutputStream outputStream;
	private InputStream inputStream;

	protected synchronized static void setConnectionAvailability(
			boolean available) {
		isValidConnectionAvailable = available;
	}

	private synchronized static boolean getConnectionAvailability() {
		return isValidConnectionAvailable;
	}

	private synchronized static String getConnectionApendix() {
		return connectionAppendix;
	}

	private synchronized static String setConnectionApendix(String apendix) {
		return connectionAppendix = apendix;
	}

	public void open(String url, int methodCode) throws IOException {

		try {
			if (!getConnectionAvailability()) {
				retestConnections();
			}
			if (!getConnectionAvailability()) {
				throw new IOException("No internet connection");
			}
			String urlWithApendix = url + getConnectionApendix();

			connection = (HttpConnection) Connector.open(urlWithApendix);
			timeoutWatchDog = new ConnectionTimeOutWatchDog(connection);

			String orginalHttpMethod = null;

			switch (methodCode) {
			case HTTP_GET:
				orginalHttpMethod = HttpConnection.GET;
				break;
			case HTTP_POST:
				orginalHttpMethod = HttpConnection.POST;
				break;
			default:
				throw new IllegalArgumentException("Http method:" + methodCode
						+ "not supported");

			}
			connection.setRequestMethod(orginalHttpMethod);
		} catch (IOException e) {
			setConnectionAvailability(false);
			throw e;
		}

	}

	public void close() throws IOException {
		cancelWatchDog();
		closeInputStream(inputStream);
		closeOutputStream(outputStream);
		closeConnection(connection);

	}

	public InputStream getInputStream() throws IOException {
		try {
			timeoutWatchDog.watch();
			inputStream = connection.openInputStream();
			timeoutWatchDog.cancel();

			return inputStream;
		} catch (IOException e) {
			setConnectionAvailability(false);
			throw e;
		}
	}

	public OutputStream getOutputStream() throws IOException {
		try {
			outputStream = new TimeoutingOutputStream(connection
					.openOutputStream(), timeoutWatchDog);
			return outputStream;
		} catch (IOException e) {
			setConnectionAvailability(false);
			throw e;
		}
	}

	public int getResponseCode() throws IOException {
		try {
			timeoutWatchDog.watch();
			int code = connection.getResponseCode();
			timeoutWatchDog.cancel();

			return code;
		} catch (IOException e) {
			setConnectionAvailability(false);
			throw e;
		}
	}

	public String getResponseHeader(String key) throws IOException {
		try {
			timeoutWatchDog.watch();
			String headerValue = connection.getHeaderField(key);
			timeoutWatchDog.cancel();
			return headerValue;
		} catch (IOException e) {
			setConnectionAvailability(false);
			throw e;
		}
	}

	public void setRequestHeader(String key, String value) throws IOException {
		try {
			connection.setRequestProperty(key, value);
		} catch (IOException e) {
			setConnectionAvailability(false);
			throw e;
		}

	}

	private void cancelWatchDog() {
		if (timeoutWatchDog != null) {
			timeoutWatchDog.cancel();
		}
	}

	private void closeInputStream(InputStream is) {
		if (is != null) {
			try {
				is.close();
			} catch (IOException e) {
				/* ignore */
			}
		}
	}

	private void closeOutputStream(OutputStream os) {
		if (os != null) {
			try {
				os.close();
			} catch (IOException e) {
				/* ignore */
			}
		}
	}

	private void closeConnection(HttpConnection connection) {
		if (connection != null) {
			try {
				connection.close();
			} catch (IOException e) {
				/* ignore */
			}
		}
	}

	private synchronized static void retestConnections() {

		Vector possiblyValidConnections = DeviceStatusHelper
				.getValidConnectionsAppendix(true, true);
		String apendix;
		setConnectionAvailability(false);

		for (int i = 0; i < possiblyValidConnections.size(); i++) {
			apendix = (String) possiblyValidConnections.elementAt(i);
			if (HttpHelper.ping(GlobalConfig.getServerUrl() + apendix)) {
				setConnectionAvailability(true);
				setConnectionApendix(apendix);
				break;
			}
		}
	}

}
